package com.example.oss.service.impl;

import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.stereotype.Service;

import com.aliyun.oss.common.utils.BinaryUtil;
import com.example.oss.service.OssCallbackService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@SuppressWarnings("deprecation")
@WebServlet(asyncSupported = true)
public class OssCallbackServiceImpl implements OssCallbackService {

    @Override
    public String GetPostBody(ServletInputStream is, int contentLen) throws Exception {
        if (contentLen > 0){
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            while (readLen != contentLen){
                readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                if (readLengthThisTime == -1){
                    break;
                }
                readLen += readLengthThisTime;
            }
            return new String(message);
        }
        return "";
    }

    @Override
    public boolean VerifyOSSCallbackData(String uri, String queryString, String authorizationInput, String pubKeyInput,
            String ossCallbackBody) throws Exception {
        byte[] authorization = BinaryUtil.fromBase64String(authorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")
                && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            System.out.println("pub key addr must be oss addrss");
            return false;
        }

        //获取public key
        String retString = executeGet(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");

        String authStr = java.net.URLDecoder.decode(uri, "UTF-8");
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + ossCallbackBody;
        return doCheck(authStr, authorization, retString);
    }

    /**
     * 获取public key
     *
     * @param url
     * @return
     */
    @SuppressWarnings({ "finally","all"})
    public String executeGet(String url) {
        BufferedReader in = null;

        String content = null;
        try {
            // 定义HttpClient
            @SuppressWarnings("resource") DefaultHttpClient client = new DefaultHttpClient();
            // 实例化HTTP方法
            HttpGet request = new HttpGet();
            request.setURI(new URI(url));
            HttpResponse response = client.execute(request);

            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            content = sb.toString();
        } catch (Exception e) {
        } finally {
            if (in != null) {
                try {
                    in.close();// 最后要关闭BufferedReader
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return content;
        }
    }

    /**
     * 验证RSA
     *
     * @param content 内容
     * @param sign 签名
     * @param publicKey 公钥
     * @return Boolean
     */
    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            return signature.verify(sign);

        } catch (Exception e) {
            log.error("OSS回调验证RSA异常：{[]}", e);
            e.printStackTrace();
        }

        return false;
    }
}
